/*
 * TOP SECRET Copyright 2006-2015 Transsion.com All right reserved. This software is the confidential and proprietary
 * information of Transsion.com ("Confidential Information"). You shall not disclose such Confidential Information and
 * shall use it only in accordance with the terms of the license agreement you entered into with Transsion.com.
 */
package com.yunji.framework_template.orm.datasource.aspect;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;

import com.yunji.framework_template.orm.datasource.DataOperateType;
import com.yunji.framework_template.orm.datasource.DataSource;
import com.yunji.framework_template.orm.datasource.DataSourceMaster;
import com.yunji.framework_template.orm.datasource.DataSourceSlave;
import com.yunji.framework_template.orm.datasource.DataSourceType;
import com.yunji.framework_template.orm.datasource.HandleDataSource;

/**
 * 执行mapper方法之前的切面 <br>
 * 获取datasource对象之前往HandleDataSource中指定当前线程数据源路由的key <br>
 * ClassName:DataSourceAspect <br/>
 * Date: 2017年8月16日 上午9:09:34 <br/>
 * 
 * @author fenglibin
 * @version
 * @see
 */
public class DataSourceAspect {

    private static final Logger               logger      = Logger.getLogger(DataSourceAspect.class);

    /**
     * 按主从分后的数据源
     */
    private Map<DataSourceType, List<String>> dataSources = new HashMap<DataSourceType, List<String>>();

    /**
     * 在mapper层方法之前获取datasource对象之前在切面中指定当前线程数据源路由的key
     */
    public void before(JoinPoint point) {

        Object target = point.getTarget();
        logger.debug("target:" + target.toString());
        String method = point.getSignature().getName();
        logger.debug("method:" + method);
        Class<?>[] classz = target.getClass().getInterfaces();
        Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()).getMethod().getParameterTypes();
        try {
            Method m = classz[0].getMethod(method, parameterTypes);
            if (m != null) {
                String methodName = m.getName();
                logger.debug("method name:" + methodName);
                /**
                 * 先判断方法本身是否有执行数据源注解，如果有则依据注解进行处理，如果没有则根据方法的类型获取数据源
                 */
                if (m.isAnnotationPresent(DataSource.class)) {
                    DataSource dataSource = m.getAnnotation(DataSource.class);
                    logger.debug("Batabase type ：" + dataSource.value());
                    HandleDataSource.putDataSource(getDataSourceName(dataSource.value()));
                } else if (m.isAnnotationPresent(DataSourceMaster.class)) {
                    logger.debug("DataSourceMaster annotation found. Set database type to master");
                    HandleDataSource.putDataSource(getDataSourceName(DataSourceType.MASTER));
                } else if (m.isAnnotationPresent(DataSourceSlave.class)) {
                    logger.debug("DataSourceSlave annotation found. Set database type to slave");
                    HandleDataSource.putDataSource(getDataSourceName(DataSourceType.SLAVE));
                } else {// 没有指定主从操作的数据库操作方法
                    if (methodName.startsWith(DataOperateType.DELETE.getName())
                        || methodName.startsWith(DataOperateType.UPDATE.getName())
                        || methodName.startsWith(DataOperateType.INSERT.getName())) {// 增删改操作路由到主库
                        logger.debug("Delete, update or insert operation found. Set database type to master");
                        HandleDataSource.putDataSource(getDataSourceName(DataSourceType.MASTER));
                    } else if (methodName.startsWith(DataOperateType.SELECT.getName())
                               || methodName.startsWith(DataOperateType.GET.getName())
                               || methodName.startsWith(DataOperateType.QUERY.getName())) {
                        logger.debug("Select, get or query operation found. Set database type to slave");
                        HandleDataSource.putDataSource(getDataSourceName(DataSourceType.SLAVE));
                    }
                }
            }
        } catch (Exception e) {
            logger.error("Aspect datasource error happened:" + e.getMessage(), e);
        }
    }

    /**
     * 获取用户的指定，获取对应的master或slave数据源。<br>
     * 如果单个种类的数据源指定了多个（如多master或多slave），会根据指定的数据源类型，任意选择一个数据源。 <br>
     * 如指定的数据源为slave，则会在多个slave中随机选择一个；同理多master的情况下也会在数据源指定为master的情况下随机返回一个master数据源。
     * 
     * @param dataSource
     * @return
     */
    private String getDataSourceName(DataSourceType dataSourceType) {
        List<String> result = dataSources.get(dataSourceType);
        if (result != null) {
            int random = (int) (result.size() * Math.random());
            return result.get(random);
        }
        return null;
    }

    /**
     * 拆分主从数据源
     * 
     * @param targetDataSources
     */
    public void setTargetDataSources(Map<String, javax.sql.DataSource> targetDataSources) {
        dataSources.put(DataSourceType.MASTER, new ArrayList<String>());
        dataSources.put(DataSourceType.SLAVE, new ArrayList<String>());
        if (targetDataSources != null && targetDataSources.size() > 0) {

            for (Map.Entry<String, javax.sql.DataSource> dataSource : targetDataSources.entrySet()) {
                String name = dataSource.getKey();
                if (name.startsWith(DataSourceType.MASTER.toString())) {
                    dataSources.get(DataSourceType.MASTER).add(name);
                } else if (name.startsWith(DataSourceType.SLAVE.toString())) {
                    dataSources.get(DataSourceType.SLAVE).add(name);
                }
            }
        }
    }

}
